"""
Integration Tests for GSICoreAnalysis.jl

Tests the complete observation processing pipeline and inter-module interactions.
"""

using Test
using Random
using Dates
using Statistics

@testset "Integration Tests" begin
    
    @testset "Mock Pipeline Integration" begin
        @info "Testing mock pipeline integration..."
        
        # Create simple mock observations
        n_obs = 20
        mock_observations = []
        
        for i in 1:n_obs
            # Create mock surface observation
            loc = (latitude=40.0 + randn(), longitude=-75.0 + randn(), pressure=1000.0 + 50*randn())
            qm = (overall_quality=0.7 + 0.3*rand(), background_check=0.8, spatial_consistency=0.7, 
                  temporal_consistency=0.6, instrument_quality=0.9)
            
            obs = (
                observation_type=:temperature,
                location=loc,
                time=now() - Hour(rand(1:6)),
                value=288.0 + 10*randn(),  # Temperature in K
                error=0.5 + 0.3*rand(),
                quality_metrics=qm,
                metadata=Dict("station_id" => "TEST$i"),
                qc_flags=[0, 0, 0, 0]
            )
            
            push!(mock_observations, obs)
        end
        
        @test length(mock_observations) == n_obs
        
        # Mock processing stages
        original_count = length(mock_observations)
        
        # Stage 1: Quality Control (filter by quality)
        qc_passed = filter(obs -> obs.quality_metrics.overall_quality > 0.6, mock_observations)
        @test length(qc_passed) ≤ original_count
        
        # Stage 2: Bias Correction (mock - add metadata)
        bias_corrected = []
        for obs in qc_passed
            bias_correction = 0.1 * randn()  # Random bias correction
            corrected_obs = merge(obs, (bias_correction=bias_correction, corrected_value=obs.value - bias_correction))
            push!(bias_corrected, corrected_obs)
        end
        @test length(bias_corrected) == length(qc_passed)
        
        # Stage 3: Spatial Processing (mock thinning - keep 80%)
        n_keep = Int(floor(0.8 * length(bias_corrected)))
        thinned = bias_corrected[1:n_keep]
        @test length(thinned) ≤ length(bias_corrected)
        
        # Stage 4: Final QC (additional quality filter)
        final_obs = filter(obs -> obs.quality_metrics.overall_quality > 0.7, thinned)
        @test length(final_obs) ≤ length(thinned)
        
        @info "Processing summary: $original_count → $(length(qc_passed)) → $(length(bias_corrected)) → $(length(thinned)) → $(length(final_obs))"
        
        # Verify processing preserved data integrity
        for obs in final_obs
            @test hasfield(typeof(obs), :bias_correction) || haskey(obs, :bias_correction)
            @test hasfield(typeof(obs), :corrected_value) || haskey(obs, :corrected_value)
            @test obs.quality_metrics.overall_quality > 0.7
        end
    end
    
    @testset "Configuration Integration" begin
        @info "Testing configuration integration..."
        
        # Test basic configuration structure
        config = (
            precision_type = Float64,
            analysis_time = DateTime(2024, 1, 15, 12, 0, 0),
            processing_mode = :research,
            parallel_processing = true,
            max_workers = 2,
            memory_limit_gb = 2.0,
            input_formats = [:bufr, :prepbufr],
            output_format = :netcdf
        )
        
        @test config.precision_type == Float64
        @test config.parallel_processing == true
        @test config.max_workers == 2
        @test config.memory_limit_gb == 2.0
        @test :bufr in config.input_formats
        @test config.output_format == :netcdf
        
        # Test configuration validation
        @test config.max_workers > 0
        @test config.memory_limit_gb > 0
        @test config.analysis_time isa DateTime
        
        @info "Configuration validation passed"
    end
    
    @testset "Performance Integration" begin
        @info "Testing performance integration..."
        
        # Create larger mock dataset
        n_large = 100
        large_dataset = []
        
        Random.seed!(123)  # For reproducible timing
        
        for i in 1:n_large
            obs = (
                value = 288.0 + 10*randn(),
                error = 0.5 + 0.3*rand(),
                quality = 0.5 + 0.5*rand(),
                location = (lat=40.0 + 20*randn(), lon=-75.0 + 30*randn()),
                metadata = Dict("id" => i)
            )
            push!(large_dataset, obs)
        end
        
        @test length(large_dataset) == n_large
        
        # Time mock processing
        start_time = time()
        
        # Mock processing steps
        step1 = filter(obs -> obs.quality > 0.6, large_dataset)
        step2 = [merge(obs, (processed=true,)) for obs in step1]
        step3 = step2[1:min(length(step2), Int(floor(0.8 * length(step2))))]  # Mock thinning
        
        processing_time = time() - start_time
        throughput = n_large / processing_time
        
        @test processing_time > 0.0
        @test throughput > 0.0
        @test length(step3) ≤ length(step2) ≤ length(step1) ≤ n_large
        
        @info "Performance: $(round(throughput, digits=1)) obs/sec, $(length(step3)) final observations"
    end
    
    @testset "Error Handling Integration" begin
        @info "Testing error handling integration..."
        
        # Test with invalid data
        invalid_obs = (
            value = NaN,  # Invalid value
            error = -1.0, # Invalid error
            quality = 2.0, # Invalid quality (>1.0)
            location = (lat=95.0, lon=200.0),  # Invalid coordinates
            metadata = Dict("status" => "corrupted")
        )
        
        # Test error detection
        @test !isfinite(invalid_obs.value)
        @test invalid_obs.error < 0
        @test invalid_obs.quality > 1.0
        @test abs(invalid_obs.location.lat) > 90
        @test abs(invalid_obs.location.lon) > 180
        
        # Test error handling function
        function validate_observation(obs)
            if !isfinite(obs.value)
                return :invalid_value
            elseif obs.error < 0
                return :invalid_error
            elseif obs.quality < 0 || obs.quality > 1
                return :invalid_quality  
            elseif abs(obs.location.lat) > 90 || abs(obs.location.lon) > 180
                return :invalid_location
            else
                return :valid
            end
        end
        
        @test validate_observation(invalid_obs) in [:invalid_value, :invalid_error, :invalid_quality, :invalid_location]
        
        # Test with valid observation
        valid_obs = (
            value = 288.0,
            error = 0.5,
            quality = 0.8,
            location = (lat=40.0, lon=-75.0),
            metadata = Dict("status" => "good")
        )
        
        @test validate_observation(valid_obs) == :valid
        
        @info "Error handling validation completed"
    end
end